﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using VsxFactory.Modeling.Repository;
using VsxFactory.Modeling.StrategyManager.Dialog.Providers;
using VsxFactory.Modeling.StrategyManager.Strategies;
using Microsoft.VisualStudio.ExtensionsExplorer.UI;
using Microsoft.VisualStudio.PlatformUI;
using DTEPackage = Microsoft.VisualStudio.Shell.Package;
using VsxFactory.Modeling.StrategyManager.Dialog;
using EnvDTE;
using System.Windows.Documents;
using System.Windows.Controls;

namespace VsxFactory.Modeling.StrategyManager.Dialog
{
    internal partial class PackageManagerWindow : DialogWindow, ILicenseWindowOpener
    {
        private Action onClosed;
        private const string F1Keyword = "vs.ExtensionManager";

        private readonly DTEPackage _ownerPackage;

        internal PackageManagerWindow(DTEPackage ownerPackage, VisualStudioStrategyContext context, IPackageSourceProvider packageSourceProvider, Action onClosedAction)
            : base(F1Keyword)
        {
            InitializeComponent();

            InsertDisclaimerElement();

            System.Diagnostics.Debug.Assert(ownerPackage != null);
            _ownerPackage = ownerPackage;
            onClosed = onClosedAction;
            SetupProviders(context, packageSourceProvider);
        }

        private void SetupProviders(VisualStudioStrategyContext context, IPackageSourceProvider packageSourceProvider)
        {
            // The ExtensionsExplorer control display providers in reverse order.
            
            var solutionProvider = new SolutionPackagesProvider(context, Resources);
            explorer.Providers.Add(solutionProvider);

            var installedProvider = new InstalledPackagesProvider(context, Resources);
            explorer.Providers.Add(installedProvider);

            var onlineProvider = new OnlineProvider(packageSourceProvider, context, Resources);
            explorer.Providers.Add(onlineProvider);

            // make the Installed provider as selected by default
            explorer.SelectedProvider = solutionProvider;
        }

        private void CanExecuteCommandOnPackage(object sender, CanExecuteRoutedEventArgs e)
        {

            if (OperationCoordinator.IsBusy)
            {
                e.CanExecute = false;
                return;
            }

            VSExtensionsExplorerCtl control = e.Source as VSExtensionsExplorerCtl;
            if (control == null)
            {
                e.CanExecute = false;
                return;
            }

            PackageItem selectedItem = control.SelectedExtension as PackageItem;
            if (selectedItem == null)
            {
                e.CanExecute = false;
                return;
            }

            try
            {
                e.CanExecute = selectedItem.IsEnabled;
            }
            catch (Exception)
            {
                e.CanExecute = false;
            }
        }

        private void ExecutedPackageCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (OperationCoordinator.IsBusy)
            {
                return;
            }

            VSExtensionsExplorerCtl control = e.Source as VSExtensionsExplorerCtl;
            if (control == null)
            {
                return;
            }

            PackageItem selectedItem = control.SelectedExtension as PackageItem;
            if (selectedItem == null)
            {
                return;
            }

            PackagesProviderBase provider = control.SelectedProvider as PackagesProviderBase;
            if (provider != null)
            {
                try
                {
                    provider.Execute(selectedItem, this);
                }
                catch (Exception exception)
                {
                    MessageHelper.ShowErrorMessage(exception);
                }
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            if (onClosed != null)
                onClosed();
        }

        private void ExecutedClose(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }

        private void ExecutedShowOptionsPage(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
            _ownerPackage.ShowOptionPage(typeof(VsxFactory.Modeling.StrategyManager.VisualStudio.ToolsOptionsPage));
        }

        private void ExecuteOpenLicenseLink(object sender, ExecutedRoutedEventArgs e)
        {
            Hyperlink hyperlink = e.OriginalSource as Hyperlink;
            if (hyperlink != null && hyperlink.NavigateUri != null)
            {
                if (hyperlink.NavigateUri == null)
                {
                    return;
                }

                // mitigate security risk
                if (hyperlink.NavigateUri.IsFile || hyperlink.NavigateUri.IsLoopback || hyperlink.NavigateUri.IsUnc)
                {
                    return;
                }

                string scheme = hyperlink.NavigateUri.Scheme;
                if (scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) ||
                    scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
                {
                    // REVIEW: Will this allow a package author to execute arbitrary program on user's machine?
                    // We have limited the url to be HTTP only, but is it sufficient?
                    System.Diagnostics.Process.Start(hyperlink.NavigateUri.AbsoluteUri);
                }

                e.Handled = true;
            }
        }

        private void ExecuteSetFocusOnSearchBox(object sender, ExecutedRoutedEventArgs e)
        {
            explorer.SetFocusOnSearchBox();
        }

        bool ILicenseWindowOpener.ShowLicenseWindow(PackageInfo package)
        {
            //if (Dispatcher.CheckAccess())
            //{
            //    return ShowLicenseWindow(package);
            //}
            //else
            //{
            //    object result = Dispatcher.Invoke(new Func<object, bool>(ShowLicenseWindow), package);
            //    return (bool)result;
            //}
            return true;
        }

        private bool ShowLicenseWindow(object dataContext)
        {
            var licenseWidow = new LicenseAcceptanceWindow()
            {
                Owner = this,
                DataContext = dataContext
            };

            bool? dialogResult = licenseWidow.ShowDialog();
            return dialogResult ?? false;
        }

        private void OnCategorySelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            PackagesTreeNodeBase selectedNode = explorer.SelectedExtensionTreeNode as PackagesTreeNodeBase;
            if (selectedNode != null)
            {
                // notify the selected node that it is opened.
                selectedNode.OnOpened();
            }
        }

        private void OnDialogWindowClosed(object sender, EventArgs e)
        {
            explorer.Providers.Clear();
        }

        /// <summary>
        /// HACK HACK: Insert the disclaimer element into the correct place inside the Explorer control. 
        /// We don't want to bring in the whole control template of the extension explorer control.
        /// </summary>
        private void InsertDisclaimerElement()
        {
            Grid grid = LogicalTreeHelper.FindLogicalNode(explorer, "resGrid") as Grid;
            if (grid != null)
            {

                // m_Providers is the name of the expander provider control (the one on the leftmost column)
                UIElement providerExpander = FindChildElementByNameOrType(grid, "m_Providers", typeof(ProviderExpander));
                if (providerExpander != null)
                {
                    // remove disclaimer text and provider extpander from their current parents
                    grid.Children.Remove(providerExpander);
                    LayoutRoot.Children.Remove(DisclaimerText);

                    // create the inner grid which will host disclaimer text and the provider extender
                    Grid innerGrid = new Grid();
                    innerGrid.RowDefinitions.Add(new RowDefinition());
                    innerGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) });

                    innerGrid.Children.Add(providerExpander);

                    Grid.SetRow(DisclaimerText, 1);
                    innerGrid.Children.Add(DisclaimerText);

                    // add the inner grid to the first column of the original grid
                    grid.Children.Add(innerGrid);
                }
            }
        }

        private UIElement FindChildElementByNameOrType(Grid parent, string childName, Type childType)
        {
            UIElement element = parent.FindName(childName) as UIElement;
            if (element != null)
            {
                return element;
            }
            else
            {
                foreach (UIElement child in parent.Children)
                {
                    if (childType.IsInstanceOfType(child))
                    {
                        return child;
                    }
                }
                return null;
            }
        }
    }

}